home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / gnustuff / tos / progut~1 / stdwin.zoo / vtrm / vtrm.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-10-18  |  33.4 KB  |  1,600 lines

  1. /*
  2.  * Virtual TeRMinal package.
  3.  * (For a description see at the end of this file.)
  4.  *
  5.  * TO DO:
  6.  *    - add interrupt handling (trminterrupt)
  7.  *    - adapt to changed window size when suspended or at SIGWINCH
  8.  *      (unfortunately, the calling module must be changed first --
  9.  *      it is not prepared for the changed window size...)
  10.  */
  11.  
  12. /*
  13.  * Includes and data definitions.
  14.  */
  15.  
  16. #include <stdio.h>
  17. #include <ctype.h>
  18.  
  19. #include "vtrm.h"
  20.  
  21. char *malloc();
  22. char *getenv();
  23. int tgetent();
  24. int tgetnum();
  25. int tgetflag();
  26. char *tgetstr();
  27. char *tgoto();
  28.  
  29. #ifdef TRACE
  30. #define Tprintf(args) fprintf args
  31. #else
  32. #define Tprintf(args) /*empty*/
  33. #endif
  34.  
  35. #ifdef lint
  36. #define VOID (void)
  37. #else
  38. #define VOID
  39. #endif
  40.  
  41. #define Forward
  42. #define Visible
  43. #define Hidden static
  44. #define Procedure
  45.  
  46. typedef char *string;
  47. typedef int bool;
  48. #define Yes 1
  49. #define No  0
  50.  
  51. #define Min(a,b) ((a) <= (b) ? (a) : (b))
  52.  
  53. /* Variables needed by termcap's tgoto() or tputs(): */
  54. char PC;
  55. char *BC;
  56. char *UP;
  57. short ospeed;
  58.  
  59. Forward int outchar();         /* procedure for termcap's tputs */
  60. #define Putstr(str)    tputs((str), 1, outchar)
  61.  
  62. /* termcap terminal capabilities */
  63.  
  64. Hidden int lines;
  65. Hidden int cols;
  66.  
  67. /*
  68.  * String-valued capabilities are saved in one big array.
  69.  * Extend this only at the end (even though it disturbs the sorting)
  70.  * because otherwise you have to change all macros...
  71.  */
  72.  
  73. #define par_al_str strcaps[0]    /* parametrized al (AL) */
  74. #define cap_cm_str strcaps[1]    /* screen-relative cursor motion (CM) */
  75. #define par_dl_str strcaps[2]    /* parametrized dl (DL) */
  76. #define al_str strcaps[3]     /* add new blank line */
  77. #define cd_str strcaps[4]     /* clear to end of display */
  78. #define ce_str strcaps[5]     /* clear to end of line */
  79. #define cl_str strcaps[6]     /* cursor home and clear screen */
  80. #define cm_str strcaps[7]     /* cursor motion */
  81. #define cp_str strcaps[8]    /* cursor position sense reply */
  82. #define cr_str strcaps[9]     /* carriage return */
  83. #define cs_str strcaps[10]     /* change scrolling region */
  84. #define dc_str strcaps[11]     /* delete character */
  85. #define dl_str strcaps[12]     /* delete line */
  86. #define dm_str strcaps[13]     /* enter delete mode */
  87. #define do_str strcaps[14]     /* cursor down one line */
  88. #define ed_str strcaps[15]     /* end delete mode */
  89. #define ei_str strcaps[16]     /* end insert mode */
  90. #define ho_str strcaps[17]    /* cursor home */
  91. #define ic_str strcaps[18]     /* insert character (if necessary; may pad) */
  92. #define im_str strcaps[19]     /* enter insert mode */
  93. #define nd_str strcaps[20]     /* cursor right (non-destructive space) */
  94. #define nl_str strcaps[21]    /* newline */
  95. #define se_str strcaps[22]     /* end standout mode */
  96. #define sf_str strcaps[23]     /* scroll text up (from bottom of region) */
  97. #define so_str strcaps[24]     /* begin standout mode */
  98. #define sp_str strcaps[25]    /* sense cursor position */
  99. #define sr_str strcaps[26]     /* scroll text down (from top of region) */
  100. #define te_str strcaps[27]     /* end termcap */
  101. #define ti_str strcaps[28]     /* start termcap */
  102. #define vb_str strcaps[29]     /* visible bell */
  103. #define ve_str strcaps[30]     /* make cursor visible again */
  104. #define vi_str strcaps[31]     /* make cursor invisible */
  105. #define le_str strcaps[32]     /* cursor left */
  106. #define bc_str strcaps[33]    /* backspace character */
  107. #define up_str strcaps[34]     /* cursor up */
  108. #define pc_str strcaps[35]    /* pad character */
  109. #define ks_str strcaps[36]    /* keypad mode start */
  110. #define ke_str strcaps[37]    /* keypad mode end */
  111. /* Insert new entries here only! Don't forget to change the next line! */
  112. #define NSTRCAPS 38 /* One more than the last entry's index */
  113.  
  114. Hidden char *strcaps[NSTRCAPS];
  115. Hidden char strcapnames[] =
  116. "ALCMDLalcdceclcmcpcrcsdcdldmdoedeihoicimndnlsesfsospsrtetivbvevilebcuppckske";
  117.  
  118. /* Same for Boolean-valued capabilities */
  119.  
  120. #define has_am flagcaps[0]    /* has automatic margins */
  121. #define has_da flagcaps[1]    /* display may be retained above screen */
  122. #define has_db flagcaps[2]    /* display may be retained below screen */
  123. #define has_in flagcaps[3]    /* not safe to have null chars on the screen */
  124. #define has_mi flagcaps[4]    /* move safely in insert (and delete?) mode */
  125. #define has_ms flagcaps[5]    /* move safely in standout mode */
  126. #define has_xs flagcaps[6]    /* standout not erased by overwriting */
  127. #define has_bs flagcaps[7]    /* terminal can backspace */
  128. #define hardcopy flagcaps[8]    /* hardcopy terminal */
  129. #define has_xn flagcaps[9]    /* Vt100 / Concept glitch */
  130. #define NFLAGS 10
  131.  
  132. Hidden char flagcaps[NFLAGS];
  133. Hidden char flagnames[]= "amdadbinmimsxsbshcxn";
  134.  
  135. Hidden Procedure
  136. getcaps(parea)
  137.     register char **parea;
  138. {
  139.     register char *capname;
  140.     register char **capvar;
  141.     register char *flagvar;
  142.  
  143.     for (capname= flagnames, flagvar= flagcaps;
  144.             *capname != '\0'; capname += 2, ++flagvar)
  145.         *flagvar= tgetflag(capname);
  146.  
  147.     for (capname= strcapnames, capvar= strcaps;
  148.             *capname != '\0'; capname += 2, ++capvar)
  149.         *capvar= tgetstr(capname, parea);
  150. }
  151.  
  152. /* terminal status */
  153.  
  154. /* calling order of Visible Procs */
  155. Hidden bool started = No;
  156.  
  157. /* to exports the capabilities mentioned in vtrm.h: */
  158. Hidden int flags = 0;
  159.  
  160. /* cost for impossible operations */
  161. #define Infinity 9999
  162.     /* Allow for adding Infinity+Infinity within range */
  163.     /* (Range is assumed at least 2**15 - 1) */
  164.  
  165. /* The following for all sorts of undefined things (except for UNKNOWN char) */
  166. #define Undefined (-1)
  167.  
  168. /* current mode of putting char's */
  169. #define Normal    0
  170. #define Insert    1
  171. #define    Delete    2
  172. Hidden short mode = Normal;
  173.  
  174. /* current standout mode */
  175. #define Off    0
  176. #define On    0200
  177. Hidden short so_mode = Off;
  178.  
  179. /* masks for char's and short's */
  180. #define NULCHAR    '\000'
  181. #define CHAR    0177
  182. #define SOBIT    On
  183. #define SOCHAR    0377
  184. /* if (has_xs) record cookies placed on screen in extra bit */
  185. /* type of cookie is determined by the SO bit */
  186. #define XSBIT    0400
  187. #define SOCOOK    0600
  188. #define COOKBITS SOCOOK
  189. #define UNKNOWN    1
  190. #define NOCOOK    UNKNOWN
  191.  
  192. /* current cursor position */
  193. Hidden short cur_y = Undefined, cur_x = Undefined;
  194.  
  195. /* "line[y][x]" holds the char on the terminal, with the SOBIT and XSBIT.
  196.  * the SOBIT tells whether the character is standing out, the XSBIT whether
  197.  * there is a cookie on the screen at this position.
  198.  * In particular a standend-cookie may be recorded AFTER the line
  199.  * (just in case some trmputdata will write after that position).
  200.  * "lenline[y]" holds the length of the line.
  201.  * Unknown chars will be 1, so the optimising compare in putline will fail.
  202.  * (Partially) empty lines are distinghuished by "lenline[y] < cols".
  203.  */
  204. Hidden short **line = 0, *lenline = 0;
  205.  
  206. /* Clear the screen initially iff only memory cursor addressing available */
  207. Hidden bool mustclear = No;
  208.  
  209. /* Make the cursor invisible when trmsync() tries to move outside the screen */
  210. Hidden bool no_cursor = No;
  211.  
  212. /* Optimise cursor motion */
  213. Hidden int abs_cost;         /* cost of absolute cursor motion */
  214. Hidden int cr_cost;         /* cost of carriage return */
  215. Hidden int do_cost;         /* cost of down */
  216. Hidden int le_cost;         /* cost of left */
  217. Hidden int nd_cost;         /* cost of right */
  218. Hidden int up_cost;         /* cost of up */
  219.  
  220. /* Optimise trailing match in put_line, iff the terminal can insert and delete
  221.  * characters; the cost per n characters will be:
  222.  *     n * MultiplyFactor + OverHead
  223.  */
  224. Hidden int ins_mf, ins_oh, del_mf, del_oh;
  225. Hidden int ed_cost, ei_cost;         /* used in move() */
  226.  
  227. /* The type of scrolling possible determines which routines get used;
  228.  * these may be:
  229.  * (1) with addline and deleteline (termcap: al_str & dl_str);
  230.  * (2) with a settable scrolling region, like VT100 (cs_str, sr_str, sf_str);
  231.  * (3) no scrolling available. (NOT YET IMPLEMENTED)
  232.  */
  233. Hidden Procedure (*scr_up)();
  234. Hidden Procedure (*scr_down)();
  235. Forward Procedure scr1up();
  236. Forward Procedure scr1down();
  237. Forward Procedure scr2up();
  238. Forward Procedure scr2down();
  239. /*Forward Procedure scr3up(); */
  240. /*Forward Procedure scr3down(); */
  241.  
  242. /*
  243.  * Starting, Ending and (fatal) Error.
  244.  */
  245.  
  246. /* 
  247.  * Initialization call.
  248.  * Determine terminal capabilities from termcap.
  249.  * Set up tty modes.
  250.  * Start up terminal and internal administration.
  251.  * Return 0 if all well, error code if in trouble.
  252.  */
  253. Visible int
  254. trmstart(plines, pcols, pflags)
  255. int *plines;
  256. int *pcols;
  257. int *pflags;
  258. {
  259.     register int err;
  260.     
  261.     Tprintf((stderr, "\ttrmstart(&li, &co, &fl);\n"));
  262.     if (started)
  263.         return TE_TWICE;
  264.     err= gettermcaps();
  265.     if (err != TE_OK)
  266.         return err;
  267.     err= setttymode();
  268.     if (err != TE_OK)
  269.         return err;
  270.     err= start_trm();
  271.     if (err != TE_OK) {
  272.         trmend();
  273.         return err;
  274.     }
  275.  
  276.     *plines = lines;
  277.     *pcols = cols;
  278.     *pflags = flags;
  279.  
  280.     started = Yes;
  281.     return TE_OK;
  282. }
  283.  
  284. /*
  285.  * Termination call.
  286.  * Reset tty modes, etc.
  287.  * Beware that it might be called by a caught interrupt even in the middle
  288.  * of trmstart()!
  289.  */
  290. Visible Procedure
  291. trmend()
  292. {
  293.     Tprintf((stderr, "\ttrmend();\n"));
  294.     set_mode(Normal);
  295.     if (so_mode != Off)
  296.         standend();
  297.     Putstr(ke_str);
  298.     Putstr(te_str);
  299.     VOID fflush(stdout);
  300.     resetttymode();
  301.  
  302.     started = No;
  303. }
  304.  
  305. /*
  306.  * Set all internal statuses to undefined, especially the contents of
  307.  * the screen, so a hard redraw will not be optimised to heaven.
  308.  */
  309. Visible Procedure
  310. trmundefined()
  311. {
  312.     register int y, x;
  313.     Tprintf((stderr, "\ttrmundefined();\n"));
  314.  
  315.     cur_y = cur_x = Undefined;
  316.     mode = so_mode = Undefined;
  317.     
  318.     for (y = 0; y < lines; y++) {
  319.         for (x = 0; x <= cols; x++)
  320.             line[y][x] = 1; /* impossible char, no so bits */
  321.         lenline[y] = cols;
  322.     }
  323. }
  324.  
  325. #ifndef NDEBUG
  326. /*ARGSUSED*/
  327. Hidden Procedure
  328. check_started(m)
  329.     char *m;
  330. {
  331.     if (!started) {
  332.         trmend();
  333.         fprintf(stderr, "bad VTRM call\n");
  334.         abort();
  335.     }
  336. }
  337. #else
  338. #define check_started(m) /*empty*/
  339. #endif
  340.  
  341. Hidden int ccc;
  342.  
  343. /*ARGSUSED*/
  344. Hidden Procedure
  345. countchar(ch)
  346. char ch;
  347. {
  348.     ccc++;
  349. }
  350.  
  351. Hidden int
  352. strcost(str)
  353. char *str;
  354. {
  355.     if (str == NULL)
  356.         return Infinity;
  357.     return str0cost(str);
  358. }
  359.  
  360. Hidden int
  361. str0cost(str)
  362. char *str;
  363. {
  364.     ccc = 0;
  365.     tputs(str, 1, countchar);
  366.     return ccc;
  367. }
  368.  
  369. /*
  370.  * Get terminal capabilities from termcap and compute related static
  371.  * properties.  Return TE_OK if all well, error code otherwise.
  372.  */
  373.  
  374. Hidden int
  375. gettermcaps() 
  376. {
  377.     string trmname;
  378.     static char tc_buf[1024]; /* Static so main program can get keydefs */
  379.     static char strbuf[1024]; /* Buffer where our caps are saved */
  380.     char *area = strbuf;
  381.     int sg;
  382.     static bool tc_initialized = No;
  383.  
  384.     if (tc_initialized)
  385.         return TE_OK;
  386.     
  387.     trmname=getenv("TERM");
  388.     if (trmname == NULL || trmname[0] == '\0')
  389.         return TE_NOTERM;
  390.     if (tgetent(tc_buf, trmname) != 1)
  391.         return TE_BADTERM;
  392.  
  393.     getcaps(&area); /* Read all flag and string type capabilities */
  394.     if (hardcopy)
  395.         return TE_DUMB;
  396.     BC = le_str;
  397.     if (BC == NULL) {
  398.         BC = bc_str;
  399.         if (BC == NULL) {
  400.             if (has_bs)
  401.                 BC = "\b";
  402.             else
  403.                 return TE_DUMB;
  404.         }
  405.     }
  406.     UP = up_str;
  407.     if (UP == NULL)
  408.         return TE_DUMB;
  409.     PC = (pc_str != NULL? pc_str[0] : NULCHAR);
  410.  
  411.     if (cm_str == NULL) {
  412.         cm_str = cap_cm_str;
  413.         if (cm_str == NULL) {
  414.             if (ho_str == NULL || do_str == NULL || nd_str == NULL)
  415.                 return TE_DUMB;
  416.         }
  417.         else
  418.             mustclear = Yes;
  419.     }
  420.     if (al_str && dl_str) {
  421.         scr_up = scr1up;
  422.         scr_down = scr1down;
  423.         flags |= CAN_SCROLL;
  424.     }
  425.     else {
  426.         if (sf_str == NULL)
  427.             sf_str = "\n";
  428.         if (cs_str && sr_str) {
  429.             scr_up = scr2up;
  430.             scr_down = scr2down;
  431.             flags |= CAN_SCROLL;
  432.         }
  433.         else
  434.             return TE_DUMB;
  435.     }
  436.         
  437.     lines = tgetnum("li");
  438.     cols = tgetnum("co");
  439.     if (lines <= 0) lines = 24;
  440.     if (cols <= 0) cols = 80;
  441.     
  442.     if ((sg=tgetnum("sg")) == 0)
  443.         has_xs = Yes;
  444.     else if (sg > 0)
  445.         return TE_DUMB;
  446.     
  447.     if (!ce_str)
  448.         return TE_DUMB;
  449.     if (cr_str == NULL) cr_str = "\r";
  450.     if (do_str == NULL) {
  451.         do_str = nl_str;
  452.         if (do_str == NULL) do_str = "\n";
  453.     }
  454.     le_str = BC;
  455.     up_str = UP;
  456.     if (vb_str == NULL)     /* then we will do with the audible bell */
  457.         vb_str = "\007";
  458.     
  459.     /* cursor sensing (non standard) */
  460.     if (cp_str != NULL && sp_str != NULL)
  461.         flags |= CAN_SENSE;
  462.  
  463.     if (so_str != NULL && se_str != NULL)
  464.         flags |= HAS_STANDOUT;
  465.  
  466.     /* calculate costs of local and absolute cursor motions */
  467.     if (cm_str == NULL)
  468.         abs_cost = Infinity;
  469.     else
  470.         abs_cost = strcost(tgoto(cm_str, 0, 0));
  471.     cr_cost = strcost(cr_str);
  472.     do_cost = strcost(do_str);
  473.     le_cost = strcost(le_str);
  474.     nd_cost = strcost(nd_str);
  475.     up_cost = strcost(up_str);
  476.  
  477.     /* cost of leaving insert or delete mode, used in move() */
  478.     ei_cost = str0cost(ei_str);
  479.     ed_cost = str0cost(ed_str);
  480.     
  481.     /* calculate insert and delete cost multiply_factor and overhead */
  482.     if (((im_str && ei_str) || ic_str) && dc_str) {
  483.         flags |= CAN_OPTIMISE;
  484.         ins_mf = 1 + str0cost(ic_str);
  485.         ins_oh = str0cost(im_str) + ei_cost;
  486.         del_mf = str0cost(dc_str);
  487.         del_oh = str0cost(dm_str) + ed_cost;
  488.     }
  489.         
  490.     tc_initialized = Yes;
  491.     return TE_OK;
  492. }
  493.  
  494. Hidden int
  495. start_trm()
  496. {
  497.     register int y;
  498.     int newlines, newcols;
  499.     
  500.     gettruewinsize(&newlines, &newcols);
  501.     if (newcols != 0 && newcols != cols ||
  502.         newlines != 0 && newlines != lines) {
  503.         /* Window size has changed.
  504.            Release previously allocated buffers. */
  505.         if (line != NULL) {
  506.             for (y= 0; y < lines; ++y)
  507.                 free((char *) line[y]);
  508.             free((char *) line);
  509.             line= NULL;
  510.         }
  511.         if (lenline != NULL) {
  512.             free((char *) lenline);
  513.             lenline= NULL;
  514.         }
  515.         if (newcols != 0)
  516.             cols = newcols;
  517.         if (newlines != 0)
  518.             lines = newlines;
  519.     }
  520.  
  521.     if (line == NULL) {
  522.         line = (short **) malloc((size_t) (lines * sizeof(short*)));
  523.         if (line == NULL)
  524.             return TE_NOMEM;
  525.         for (y = 0; y < lines; y++) {
  526.             line[y] = (short *)
  527.                 malloc((size_t) ((cols+1) * sizeof(short)));
  528.             if (line == NULL)
  529.                 return TE_NOMEM;
  530.         }
  531.     }
  532.     if (lenline == NULL) {
  533.         lenline = (short *) malloc((size_t) (lines * sizeof(short)));
  534.         if (lenline == NULL)
  535.             return TE_NOMEM;
  536.     }
  537.  
  538.     trmundefined();
  539.  
  540.     Putstr(ti_str);
  541.     Putstr(ks_str);
  542.     if (cs_str)
  543.         Putstr(tgoto(cs_str, lines-1, 0));
  544.     if (mustclear)
  545.         clear_lines(0, lines-1);
  546.     return TE_OK;
  547. }
  548.  
  549.  
  550. /*
  551.  * Sensing and moving the cursor.
  552.  */
  553.  
  554. /*
  555.  * Sense the current (y, x) cursor position, after a possible manual
  556.  * change by the user with local cursor motions.
  557.  * If the terminal cannot be asked for the current cursor position,
  558.  * or if the string returned by the terminal is garbled,
  559.  * the position is made Undefined.
  560.  */
  561.  
  562. Visible Procedure
  563. trmsense(py, px)
  564.     int *py;
  565.     int *px;
  566. {
  567.     bool getpos();
  568.  
  569.     Tprintf((stderr, "\ttrmsense(&yy, &xx);\n"));
  570.     check_started("trmsense");
  571.  
  572.     *py = *px = Undefined;
  573.     set_mode(Normal);
  574.     if (so_mode != Off)
  575.         standend();
  576.     
  577.     if (flags&CAN_SENSE && getpos(py, px)) {
  578.         if (*py < 0 || lines <= *py || *px < 0 || cols <= *px)
  579.             *py = *px = Undefined;
  580.     }
  581.     cur_y = Undefined;
  582.     cur_x = Undefined;
  583. }
  584.  
  585. Hidden bool
  586. getpos(py, px)
  587. int *py, *px;
  588. {
  589.     char *format = cp_str;
  590.     int fc;         /* current format character */
  591.     int ic;         /* current input character */
  592.     int num;
  593.     int on_y = 1;
  594.     bool incr_orig = No;
  595.     int i, ni;
  596.  
  597.     Putstr(sp_str);
  598.     VOID fflush(stdout);
  599.  
  600.     while (fc = *format++) {
  601.         if (fc != '%') {
  602.             if (trminput() != fc)
  603.                 return No;
  604.         }
  605.         else {
  606.             switch (fc = *format++) {
  607.             case '%':
  608.                 if (trminput() != '%')
  609.                     return No;
  610.                 continue;
  611.             case 'r':
  612.                 on_y = 1 - on_y;
  613.                 continue;
  614.             case 'i':
  615.                 incr_orig = Yes;
  616.                 continue;
  617.             case 'd':
  618.                 ic = trminput();
  619.                 if (!isdigit(ic))
  620.                     return No;
  621.                 num = ic - '0';
  622.                 while (isdigit(ic=trminput()))
  623.                     num = 10*num + ic - '0';
  624.                 trmpushback(ic);
  625.                 break;
  626.             case '2':
  627.             case '3':
  628.                 ni = fc - '0';
  629.                     num = 0;
  630.                 for (i=0; i<ni; i++) {
  631.                     ic = trminput();
  632.                     if (isdigit(ic))
  633.                         num = 10*num + ic - '0';
  634.                     else
  635.                         return No;
  636.                 }
  637.                 break;
  638.             case '+':
  639.                 num = trminput() - *format++;
  640.                 break;
  641.             case '-':
  642.                 num = trminput() + *format++;
  643.                 break;
  644.             default:
  645.                 return No;
  646.             }
  647.             /* assign num to parameter */
  648.             if (incr_orig)
  649.                 num--;
  650.             if (on_y)
  651.                 *py = num;
  652.             else
  653.                 *px = num;
  654.             on_y = 1 - on_y;
  655.         }
  656.     }
  657.  
  658.     return Yes;
  659. }
  660.         
  661. /* 
  662.  * To move over characters by rewriting them, we have to check:
  663.  * (1) that the screen has been initialised on these positions;
  664.  * (2) we do not screw up characters
  665.  * when rewriting line[y] from x_from upto x_to
  666.  */
  667. Hidden bool
  668. rewrite_ok(y, xfrom, xto)
  669. int y, xfrom, xto;
  670. {
  671.     register short *plnyx, *plnyto;
  672.     
  673.     if (xto > lenline[y])
  674.         return No;
  675.  
  676.     plnyto = &line[y][xto];
  677.     for (plnyx = &line[y][xfrom]; plnyx <= plnyto; plnyx++)
  678.         if (*plnyx == UNKNOWN
  679.             ||
  680.             (!has_xs && (*plnyx & SOBIT) != so_mode)
  681.            )
  682.             return No;
  683.     return Yes;
  684. }
  685.         
  686. /*
  687.  * Move to position y,x on the screen
  688.  */
  689. /* possible move types for y and x respectively: */
  690. #define None    0
  691. #define Down    1
  692. #define Up    2
  693. #define Right    1
  694. #define ReWrite    2
  695. #define Left    3
  696. #define CrWrite    4
  697.  
  698. Hidden Procedure
  699. move(y, x)
  700. int y, x;
  701. {
  702.     int dy, dx;
  703.     int y_cost, x_cost, y_move, x_move;
  704.     int mode_cost;
  705.     int xi;
  706.     
  707.     if (cur_y == y && cur_x == x)
  708.         return;
  709.     
  710.     if (!has_mi || mode == Undefined)
  711.         set_mode(Normal);
  712.     if (!has_xs && ((!has_ms && so_mode != Off) || so_mode == Undefined))
  713.         standend();
  714.     
  715.     if (cur_y == Undefined || cur_x == Undefined)
  716.         goto absmove;
  717.     
  718.     dy = y - cur_y;
  719.     dx = x - cur_x;
  720.  
  721.     if (dy > 0) {
  722.         y_move = Down;
  723.         y_cost = dy * do_cost;
  724.     }
  725.     else if (dy < 0) {
  726.         y_move = Up;
  727.         y_cost = -dy * up_cost;
  728.     }
  729.     else {
  730.         y_move = None;
  731.         y_cost = 0;
  732.     }
  733.     if (y_cost < abs_cost) {
  734.         switch (mode) {
  735.         case Normal:
  736.             mode_cost = 0;
  737.             break;
  738.         case Insert:
  739.             mode_cost = ei_cost;
  740.             break;
  741.         case Delete:
  742.             mode_cost = ed_cost;
  743.             break;
  744.         }
  745.         if (dx > 0) {
  746.             x_cost = dx + mode_cost;
  747.             if (dx*nd_cost < x_cost || !rewrite_ok(y, cur_x, x)) {
  748.                 x_cost = dx * nd_cost;
  749.                 x_move = Right;
  750.             }
  751.             else
  752.                 x_move = ReWrite;
  753.         }
  754.         else if (dx < 0) {
  755.             x_cost = -dx * le_cost;
  756.             x_move = Left;
  757.         }
  758.         else {
  759.             x_cost = 0;
  760.             x_move = None;
  761.         }
  762.         if (cr_cost + x + mode_cost < x_cost && rewrite_ok(y, 0, x)) {
  763.             x_move = CrWrite;
  764.             x_cost = cr_cost + x + mode_cost;
  765.         }
  766.     }
  767.     else
  768.         x_cost = abs_cost;
  769.  
  770.     if (y_cost + x_cost < abs_cost) {
  771.         switch (y_move) {
  772.         case Down:
  773.             while (dy-- > 0) Putstr(do_str);
  774.             break;
  775.         case Up:
  776.             while (dy++ < 0) Putstr(up_str);
  777.             break;
  778.         }
  779.         switch (x_move) {
  780.         case Right:
  781.             while (dx-- > 0) Putstr(nd_str);
  782.             break;
  783.         case Left:
  784.             while (dx++ < 0) Putstr(le_str);
  785.             break;
  786.         case CrWrite:
  787.             Putstr(cr_str);
  788.             cur_x = 0;
  789.             /* FALL THROUGH */
  790.         case ReWrite:
  791.             set_mode(Normal);
  792.             for (xi = cur_x; xi < x; xi++)
  793.                 putchar(line[y][xi]);
  794.             break;
  795.         }
  796.     }
  797.     else
  798.     {
  799.     absmove:
  800.         if (cm_str == NULL) {
  801.             Putstr(ho_str);
  802.             for (cur_y = 0; cur_y < y; ++cur_y)
  803.                 Putstr(do_str);
  804.             /* Should try to use tabs here: */
  805.             for (cur_x = 0; cur_x < x; ++cur_x)
  806.                 Putstr(nd_str);
  807.         }
  808.         else
  809.             Putstr(tgoto(cm_str, x, y));
  810.     }
  811.     
  812.     cur_y = y;
  813.     cur_x = x;
  814. }
  815.  
  816.  
  817. /*
  818.  * Putting data on the screen.
  819.  */
  820.  
  821. /*
  822.  * Fill screen area with given data.
  823.  * Characters with the SO-bit (0200) set are put in standout mode.
  824.  */
  825. Visible Procedure
  826. trmputdata(yfirst, ylast, indent, data)
  827. int yfirst;
  828. int ylast;
  829. register int indent;
  830. register string data;
  831. {
  832.     register int y;
  833.     int x, len, lendata, space;
  834.         
  835.     Tprintf((stderr, "\ttrmputdata(%d, %d, %d, \"%s\");\n", yfirst, ylast, indent, data));
  836.     check_started("trmputdata");
  837.     
  838.     if (yfirst < 0)
  839.         yfirst = 0;
  840.     if (ylast >= lines)
  841.         ylast = lines-1;
  842.     space = cols*(ylast-yfirst+1) - indent;
  843.     if (space <= 0)
  844.         return;
  845.     yfirst += indent/cols;
  846.     indent %= cols;
  847.     y= yfirst;
  848.     if (!data)
  849.         data= ""; /* Safety net */
  850.     x = indent;
  851.     lendata = (int)strlen(data);
  852.     if (ylast == lines-1 && lendata >= space)
  853.         lendata = space - 1;
  854.     len = Min(lendata, cols-x);
  855.     while (y <= ylast) {
  856.         put_line(y, x, data, len);
  857.         y++;
  858.         lendata -= len;
  859.         if (lendata > 0) {
  860.             x = 0;
  861.             data += len;
  862.             len = Min(lendata, cols);
  863.         }
  864.         else
  865.             break;
  866.     }
  867.     if (y <= ylast)
  868.         clear_lines(y, ylast);
  869. }
  870.  
  871. /* 
  872.  * We will first try to get the picture:
  873.  *
  874.  *                  op>>>>>>>>>>>op          oq<<<<<<<<<<<<<<<<<<<<<<<<oq
  875.  *                  ^            ^           ^                         ^
  876.  *           <xskip><-----m1----><----od-----><-----------m2----------->
  877.  *   OLD:   "You're in a maze of twisty little pieces of code, all alike"
  878.  *   NEW:          "in a maze of little twisting pieces of code, all alike"
  879.  *                  <-----m1----><-----nd------><-----------m2----------->
  880.  *                  ^            ^             ^                         ^
  881.  *                  np>>>>>>>>>>>np            nq<<<<<<<<<<<<<<<<<<<<<<<<nq
  882.  * where
  883.  *    op, oq, np, nq are pointers to start and end of Old and New data,
  884.  * and
  885.  *    xskip = length of indent to be skipped,
  886.  *    m1 = length of Matching part at start,
  887.  *    od = length of Differing mid on screen,
  888.  *    nd = length of Differing mid in data to be put,
  889.  *    m2 = length of Matching trail.
  890.  *
  891.  * Then we will try to find a long blank-or-cleared piece in <nd+m2>:
  892.  *
  893.  *    <---m1---><---d1---><---nb---><---d2---><---m2--->
  894.  *              ^         ^         ^        ^         ^
  895.  *              np        bp        bq1      nq        nend
  896.  * where
  897.  *    bp, bq are pointers to start and AFTER end of blank piece,
  898.  * and
  899.  *    d1 = length of differing part before blank piece,
  900.  *    nb = length of blank piece to be skipped,
  901.  *    d2 = length of differing part after blank piece.
  902.  * Remarks:
  903.  *    d1 + nb + d2 == nd,
  904.  * and
  905.  *    d2 maybe less than 0.
  906.  */
  907. Hidden int
  908. put_line(y, xskip, data, len)
  909. int y, xskip;
  910. string data;
  911. int len;
  912. {
  913.     register short *op, *oq;
  914.     register char *np, *nq, *nend;
  915.     char *bp, *bq1, *p, *q;
  916.     int m1, m2, od, nd, delta, dd, d1, nb, d2;
  917.     bool skipping;
  918.     int cost, o_cost;     /* normal and optimising cost */
  919.     
  920.     /* Bugfix GvR 19-June-87: */
  921.     while (lenline[y] < xskip)
  922.         line[y][lenline[y]++] = ' ';
  923.     
  924.     /* calculate the magic parameters */
  925.     op = &line[y][xskip];
  926.     oq = &line[y][lenline[y]-1];
  927.     np = data;
  928.     nq = nend = data + len - 1;
  929.     m1 = m2 = 0;
  930.     while ((*op&SOCHAR) == (((short)*np)&SOCHAR) && op <= oq && np <= nq)
  931.         op++, np++, m1++;
  932.     if (flags & CAN_OPTIMISE)
  933.         while ((*oq&SOCHAR) == (((short)*nq)&SOCHAR) && op <= oq && np <= nq)
  934.             oq--, nq--, m2++;
  935.     od = oq - op + 1;
  936.     nd = nq - np + 1;
  937.     /* now we have the first picture above */
  938.  
  939.     if (od==0 && nd==0)
  940.         return;
  941.     delta = nd - od;
  942.  
  943.     /* find the blank piece */
  944.     p = q = bp = bq1 = np;
  945.     oq += m2;         /* back to current eol */
  946.     if (!has_in) {
  947.         while (p <= nend) {
  948.             while (q<=nend && *q==' ' && (op>oq || *op==' '))
  949.                 q++, op++;
  950.             if (q - p > bq1 - bp)
  951.                 bp = p, bq1 = q;
  952.             p = ++q;
  953.             op++;
  954.         }
  955.     }
  956.     d1 = bp - np;
  957.     nb = bq1 - bp;
  958.     d2 = nq - bq1 + 1;
  959.     
  960.     /* what is cheapest:
  961.      *    normal: put nd+m2;                         (dd = nd+m2)
  962.      *    skipping: put d1, skip nb, put d2+m2;      (dd = d2+m2)
  963.      *    optimise: put dd, insert or delete delta.  (dd = min(od,nd))
  964.      */
  965.     cost = nd + m2;     /* normal cost */
  966.     if (nb > abs_cost || (d1 == 0 && nb > 0)) {
  967.         skipping = Yes;
  968.         cost -= nb - (d1>0 ? abs_cost : 0); /* skipping cost */
  969.         dd = d2;
  970.     }
  971.     else {
  972.         skipping = No;
  973.         dd = nd;
  974.     }
  975.     
  976.     if (m2 != 0) {
  977.         /* try optimising */
  978.         o_cost = Min(od, nd);
  979.         if (delta > 0)
  980.             o_cost += delta * ins_mf + ins_oh;
  981.         else if (delta < 0)
  982.             o_cost += -delta * del_mf + del_oh;
  983.         if (o_cost >= cost) {
  984.             /* discard m2, no optimise */
  985.             dd += m2;
  986.             m2 = 0;
  987.         }
  988.         else {
  989.             dd = Min(od, nd);
  990.             skipping = No;
  991.         }
  992.     }
  993.  
  994.     /* and now for the real work */
  995.     if (!skipping || d1 > 0)
  996.         move(y, xskip + m1);
  997.  
  998.     if (has_xs)
  999.         get_so_mode();
  1000.     
  1001.     if (skipping) {
  1002.         if (d1 > 0) {
  1003.             set_mode(Normal);
  1004.             put_str(np, d1, No);
  1005.         }
  1006.         if (has_xs && so_mode != Off)
  1007.             standend();
  1008.         set_blanks(y, xskip+m1+d1, xskip+m1+d1+nb);
  1009.         if (dd != 0 || delta < 0) {
  1010.             move(y, xskip+m1+d1+nb);
  1011.             np = bq1;
  1012.         }
  1013.     }
  1014.     
  1015.     if (dd > 0) {
  1016.         set_mode(Normal);
  1017.         put_str(np, dd, No);
  1018.     }
  1019.     
  1020.     if (m2 > 0) {
  1021.         if (delta > 0) {
  1022.             set_mode(Insert);
  1023.             ins_str(np+dd, delta);
  1024.         }
  1025.         else if (delta < 0) {
  1026.             if (so_mode != Off)
  1027.                 standend();
  1028.                 /* Some terminals fill with standout spaces! */
  1029.             set_mode(Delete);
  1030.             del_str(-delta);
  1031.         }
  1032.     }
  1033.     else {
  1034.         if (delta < 0) {
  1035.             clr_to_eol();
  1036.             return;
  1037.         }
  1038.     }
  1039.     
  1040.     lenline[y] = xskip + len;
  1041.     if (cur_x == cols) {
  1042.         if (!has_mi)
  1043.             set_mode(Normal);
  1044.         if (!has_ms)
  1045.             so_mode = Undefined;
  1046.         if (has_am) {
  1047.             if (has_xn)
  1048.                 cur_y= Undefined;
  1049.             else
  1050.                 cur_y++;
  1051.         }
  1052.         else
  1053.             Putstr(cr_str);
  1054.         cur_x = 0;
  1055.     }
  1056.     else if (has_xs) {
  1057.         if (m2 == 0) {
  1058.             if (so_mode == On)
  1059.                 standend();
  1060.         }
  1061.         else {
  1062.             if (!(line[cur_y][cur_x] & XSBIT)) {
  1063.                 if (so_mode != (line[cur_y][cur_x] & SOBIT))
  1064.                     (so_mode ? standend() : standout());
  1065.             }
  1066.         }
  1067.     }
  1068. }
  1069.  
  1070. Hidden Procedure
  1071. set_mode(m)
  1072. int m;
  1073. {
  1074.     if (m == mode)
  1075.         return;
  1076.     switch (mode) {
  1077.     case Insert:
  1078.         Putstr(ei_str);
  1079.         break;
  1080.     case Delete:
  1081.         Putstr(ed_str);
  1082.         break;
  1083.     case Undefined:
  1084.         Putstr(ei_str);
  1085.         Putstr(ed_str);
  1086.         break;
  1087.     }
  1088.     switch (m) {
  1089.     case Insert:
  1090.         Putstr(im_str);
  1091.         break;
  1092.     case Delete:
  1093.         Putstr(dm_str);
  1094.         break;
  1095.     }
  1096.     mode = m;
  1097. }
  1098.  
  1099. Hidden Procedure
  1100. get_so_mode()
  1101. {
  1102.     if (cur_x >= lenline[cur_y] || line[cur_y][cur_x] == UNKNOWN)
  1103.         so_mode = Off;
  1104.     else
  1105.         so_mode = line[cur_y][cur_x] & SOBIT;
  1106. }
  1107.  
  1108. Hidden Procedure
  1109. standout()
  1110. {
  1111.     Putstr(so_str);
  1112.     so_mode = On;
  1113.     if (has_xs)
  1114.         line[cur_y][cur_x] |= SOCOOK;
  1115. }
  1116.  
  1117. Hidden Procedure
  1118. standend()
  1119. {
  1120.     Putstr(se_str);
  1121.     so_mode = Off;
  1122.     if (has_xs)
  1123.         line[cur_y][cur_x] = (line[cur_y][cur_x] & ~SOBIT) | XSBIT;
  1124. }
  1125.  
  1126. Hidden Procedure
  1127. put_str(data, n, inserting)
  1128. char *data;
  1129. int n;
  1130. bool inserting;
  1131. {
  1132.     register short c, so;
  1133.     short *ln_y_x, *ln_y_end;
  1134.     
  1135.     so = so_mode;
  1136.     if (has_xs) {
  1137.         ln_y_x = &line[cur_y][cur_x];
  1138.         ln_y_end = &line[cur_y][lenline[cur_y]];
  1139.     }
  1140.     while (n-- > 0) {
  1141.         if (has_xs && ln_y_x <= ln_y_end && ((*ln_y_x)&XSBIT))
  1142.             so = so_mode = (*ln_y_x)&SOBIT;
  1143.             /* this also checks for the standend cookie AFTER */
  1144.             /* the line because off the equals sign in <= */
  1145.         c = ((short)(*data++))&SOCHAR;
  1146.         if ((c&SOBIT) != so) {
  1147.             so = c&SOBIT;
  1148.             so ? standout() : standend();
  1149.          }
  1150.         if (inserting)
  1151.             Putstr(ic_str);
  1152.         put_c(c);
  1153.         if (has_xs)
  1154.             ln_y_x++;
  1155.     }
  1156. }
  1157.  
  1158. Hidden Procedure
  1159. ins_str(data, n)
  1160. char *data;
  1161. int n;
  1162. {
  1163.     int x;
  1164.     
  1165.     /* x will start AFTER the line, because there might be a cookie */
  1166.     for (x = lenline[cur_y]; x >= cur_x; x--)
  1167.         line[cur_y][x+n] = line[cur_y][x];
  1168.     put_str(data, n, Yes);
  1169. }
  1170.  
  1171. Hidden Procedure
  1172. del_str(n)
  1173. int n;
  1174. {
  1175.     int x, xto;
  1176.     
  1177.     xto = lenline[cur_y] - n; /* again one too far because of cookie */
  1178.     if (has_xs) {
  1179.         for (x = cur_x + n; x >= cur_x; x--) {
  1180.             if (line[cur_y][x] & XSBIT)
  1181.                 break;
  1182.         }
  1183.         if (x >= cur_x)
  1184.             line[cur_y][cur_x+n] =
  1185.                 (line[cur_y][cur_x+n] & CHAR)
  1186.                 |
  1187.                 (line[cur_y][x] & COOKBITS);
  1188.     }
  1189.     for (x = cur_x; x <= xto; x++)
  1190.         line[cur_y][x] = line[cur_y][x+n];
  1191.     while (n-- > 0)
  1192.         Putstr(dc_str);
  1193. }
  1194.  
  1195. Hidden Procedure
  1196. put_c(c)
  1197. int c;
  1198. {
  1199.     char ch;
  1200.     short xs_flag;
  1201.     
  1202.     ch = c&CHAR;
  1203.     if (!isprint(ch) && ch != ' ') /* V7 isprint doesn't include blank */
  1204.         ch= '?';
  1205.     putchar(ch);
  1206.     if (has_xs)
  1207.         xs_flag = line[cur_y][cur_x]&XSBIT;
  1208.     else
  1209.         xs_flag = 0;
  1210.     line[cur_y][cur_x] = (c&SOCHAR)|xs_flag;
  1211.     cur_x++;
  1212. }
  1213.  
  1214. Hidden Procedure
  1215. clear_lines(yfirst, ylast)
  1216. int yfirst, ylast ;
  1217. {
  1218.     register int y;
  1219.     
  1220.     if (!has_xs && so_mode != Off)
  1221.         standend();
  1222.     if (cl_str && yfirst == 0 && ylast == lines-1) {
  1223.         Putstr(cl_str);
  1224.         cur_y = cur_x = 0;
  1225.         for (y = 0; y < lines; ++y) {
  1226.             lenline[y] = 0;
  1227.             if (has_xs) line[y][0] = NOCOOK;
  1228.         }
  1229.         return;
  1230.     }
  1231.     for (y = yfirst; y <= ylast; y++) {
  1232.         if (lenline[y] > 0) {
  1233.             move(y, 0);
  1234.             if (ylast == lines-1 && cd_str) {
  1235.                 Putstr(cd_str);
  1236.                 while (y <= ylast) {
  1237.                     if (has_xs) line[y][0] = NOCOOK;
  1238.                     lenline[y++] = 0;
  1239.                 }
  1240.                 break;
  1241.             }
  1242.             else {
  1243.                 clr_to_eol();
  1244.             }
  1245.         }
  1246.     }
  1247. }
  1248.  
  1249. Hidden Procedure
  1250. clr_to_eol()
  1251. {
  1252.     lenline[cur_y] = cur_x;
  1253.     if (!has_xs && so_mode != Off)
  1254.         standend();
  1255.     Putstr(ce_str);
  1256.     if (has_xs) {
  1257.         if (cur_x == 0)
  1258.             line[cur_y][0] = NOCOOK;
  1259.         else if (line[cur_y][cur_x-1]&SOBIT)
  1260.             standend();
  1261.     }
  1262. }
  1263.  
  1264. Hidden Procedure
  1265. set_blanks
  1266. (y, xfrom, xto)
  1267. int y, xfrom, xto;
  1268. {
  1269.     register int x;
  1270.     
  1271.     for (x = xfrom; x < xto; x++) {
  1272.         line[y][x] = (line[y][x]&XSBIT) | ' ';
  1273.     }
  1274. }
  1275.  
  1276. /* 
  1277.  * outchar() is used by termcap's tputs;
  1278.  * we can't use putchar because that's probably a macro
  1279.  */
  1280. Hidden int
  1281. outchar(ch)
  1282. char ch;
  1283. {
  1284.     putchar(ch);
  1285. }
  1286.  
  1287. /*
  1288.  * Scrolling (part of) the screen up (or down, dy<0).
  1289.  */
  1290.  
  1291. Visible Procedure
  1292. trmscrollup(yfirst, ylast, by)
  1293. register int yfirst;
  1294. register int ylast;
  1295. register int by;
  1296. {
  1297.     Tprintf((stderr, "\ttrmscrollup(%d, %d, %d);\n", yfirst, ylast, by));
  1298.     check_started("trmscrollup");
  1299.     
  1300.     if (yfirst < 0)
  1301.         yfirst = 0;
  1302.     if (ylast >= lines)
  1303.         ylast = lines-1;
  1304.  
  1305.     if (yfirst > ylast)
  1306.         return;
  1307.  
  1308.     if (!has_xs && so_mode != Off)
  1309.         standend();
  1310.     
  1311.     if (by > 0 && yfirst + by > ylast
  1312.         ||
  1313.         by < 0 && yfirst - by > ylast)
  1314.     {
  1315.         clear_lines(yfirst, ylast);
  1316.         return;
  1317.     }
  1318.     
  1319.     if (by > 0) {
  1320.         (*scr_up)(yfirst, ylast, by);
  1321.         scr_lines(yfirst, ylast, by, 1);
  1322.     }
  1323.     else if (by < 0) {
  1324.         (*scr_down)(yfirst, ylast, -by);
  1325.         scr_lines(ylast, yfirst, -by, -1);
  1326.     }
  1327. }
  1328.  
  1329. Hidden Procedure
  1330. scr_lines(yfrom, yto, n, dy)
  1331. int yfrom, yto, n, dy;
  1332. {
  1333.     register int y;
  1334.     short *saveln;
  1335.     
  1336.     while (n-- > 0) {
  1337.         saveln = line[yfrom];
  1338.         for (y = yfrom; y != yto; y += dy) {
  1339.             line[y] = line[y+dy];
  1340.             lenline[y] = lenline[y+dy];
  1341.         }
  1342.         line[yto] = saveln;
  1343.         lenline[yto] = 0;
  1344.         if (has_xs) line[yto][0] = NOCOOK;
  1345.     }
  1346. }
  1347.  
  1348. Hidden Procedure
  1349. scr1up(yfirst, ylast, n)
  1350.     int yfirst;
  1351.     int ylast;
  1352.     int n;
  1353. {
  1354.     move(yfirst, 0);
  1355.     dellines(n);
  1356.     if (ylast < lines-1) {
  1357.         move(ylast-n+1, 0);
  1358.         addlines(n);
  1359.     }
  1360. }
  1361.  
  1362.  
  1363. Hidden Procedure
  1364. scr1down(yfirst, ylast, n)
  1365.     int yfirst;
  1366.     int ylast;
  1367.     int n;
  1368. {
  1369.     if (ylast == lines-1) {
  1370.         clear_lines(ylast-n+1, ylast);
  1371.     }
  1372.     else {
  1373.         move(ylast-n+1, 0);
  1374.         dellines(n);
  1375.     }
  1376.     move(yfirst, 0);
  1377.     addlines(n);
  1378. }
  1379.  
  1380.  
  1381. Hidden Procedure
  1382. addlines(n)
  1383. register int n;
  1384. {
  1385.     if (par_al_str && n > 1)
  1386.             Putstr(tgoto(par_al_str, n, n));
  1387.     else {
  1388.         while (n-- > 0)
  1389.             Putstr(al_str);
  1390.     }
  1391. }
  1392.  
  1393.  
  1394. Hidden Procedure
  1395. dellines(n)
  1396. register int n;
  1397. {
  1398.     if (par_dl_str && n > 1)
  1399.         Putstr(tgoto(par_dl_str, n, n));
  1400.     else {
  1401.         while (n-- > 0)
  1402.             Putstr(dl_str);
  1403.     }
  1404. }
  1405.  
  1406.  
  1407. Hidden Procedure
  1408. scr2up(yfirst, ylast, n)
  1409. int yfirst, ylast, n;
  1410. {
  1411.     Putstr(tgoto(cs_str, ylast, yfirst));
  1412.     cur_y = cur_x = Undefined;
  1413.     move(ylast, 0);
  1414.     while (n-- > 0) {
  1415.         Putstr(sf_str);
  1416.         if (has_db && ylast == lines-1)
  1417.             clr_to_eol();
  1418.     }
  1419.     Putstr(tgoto(cs_str, lines-1, 0));
  1420.     cur_y = cur_x = Undefined;
  1421. }
  1422.  
  1423.  
  1424. Hidden Procedure
  1425. scr2down(yfirst, ylast, n)
  1426. int yfirst, ylast, n;
  1427. {
  1428.     Putstr(tgoto(cs_str, ylast, yfirst));
  1429.     cur_y = cur_x = Undefined;
  1430.     move(yfirst, 0);
  1431.     while (n-- > 0) {
  1432.         Putstr(sr_str);
  1433.         if (has_da && yfirst == 0)
  1434.             clr_to_eol();
  1435.     }
  1436.     Putstr(tgoto(cs_str, lines-1, 0));
  1437.     cur_y = cur_x = Undefined;
  1438. }
  1439.  
  1440.  
  1441. /*
  1442.  * Synchronization, move cursor to given position (or previous if < 0).
  1443.  */
  1444.  
  1445. Visible Procedure
  1446. trmsync(y, x)
  1447.     int y;
  1448.     int x;
  1449. {
  1450.     Tprintf((stderr, "\ttrmsync(%d, %d);\n", y, x));
  1451.     check_started("trmsync");
  1452.     
  1453.     if (0 <= y && y < lines && 0 <= x && x < cols) {
  1454.         move(y, x);
  1455.         if (no_cursor) {
  1456.             Putstr(ve_str);
  1457.             no_cursor = No;
  1458.         }
  1459.     }
  1460.     else if (no_cursor == No) {
  1461.         Putstr(vi_str);
  1462.         no_cursor = Yes;
  1463.     }
  1464.     VOID fflush(stdout);
  1465. }
  1466.  
  1467.  
  1468. /*
  1469.  * Send a bell, visible if possible.
  1470.  */
  1471.  
  1472. Visible Procedure
  1473. trmbell()
  1474. {
  1475.     Tprintf((stderr, "\ttrmbell();\n"));
  1476.     check_started("trmbell");
  1477.     
  1478.     Putstr(vb_str);
  1479.     VOID fflush(stdout);
  1480. }
  1481.  
  1482.  
  1483. #ifdef SHOW
  1484.  
  1485. /*
  1486.  * Show the current internal statuses of the screen on stderr.
  1487.  * For debugging only.
  1488.  */
  1489.  
  1490. Visible Procedure
  1491. trmshow(s)
  1492. char *s;
  1493. {
  1494.     int y, x;
  1495.     
  1496.     fprintf(stderr, "<<< %s >>>\n", s);
  1497.     for (y = 0; y < lines; y++) {
  1498.         for (x = 0; x <= lenline[y] /*** && x < cols-1 ***/ ; x++) {
  1499.             fputc(line[y][x]&CHAR, stderr);
  1500.         }
  1501.         fputc('\n', stderr);
  1502.         for (x = 0; x <= lenline[y] && x < cols-1; x++) {
  1503.             if (line[y][x]&SOBIT)
  1504.                 fputc('-', stderr);
  1505.             else
  1506.                 fputc(' ', stderr);
  1507.         }
  1508.         fputc('\n', stderr);
  1509.         for (x = 0; x <= lenline[y] && x < cols-1; x++) {
  1510.             if (line[y][x]&XSBIT)
  1511.                 fputc('+', stderr);
  1512.             else
  1513.                 fputc(' ', stderr);
  1514.         }
  1515.         fputc('\n', stderr);
  1516.     }
  1517.     fprintf(stderr, "CUR_Y = %d, CUR_X = %d.\n", cur_y, cur_x);
  1518.     VOID fflush(stderr);
  1519. }
  1520. #endif
  1521.  
  1522.  
  1523. /*
  1524.  * DESCRIPTION.
  1525.  *
  1526.  * This package uses termcap to determine the terminal capabilities.
  1527.  *
  1528.  * The lines and columns of our virtual terminal are numbered 
  1529.  *    y = {0...lines-1} from top to bottom, and
  1530.  *    x = {0...cols-1} from left to right,
  1531.  * respectively.
  1532.  *
  1533.  * The Visible Procedures in this package are:
  1534.  *
  1535.  * trmstart(&lines, &cols, &flags)
  1536.  *     Obligatory initialization call (sets tty modes etc.),
  1537.  *     Returns the height and width of the screen to the integers
  1538.  *     whose addresses are passed as parameters, and a flag that
  1539.  *    describes some capabilities.
  1540.  *    Function return value: 0 if all went well, an error code if there
  1541.  *    is any trouble.  No messages are printed for errors.
  1542.  *
  1543.  * trmundefined()
  1544.  *    Sets internal representation of screen and attributes to undefined.
  1545.  *    This is necessary for a hard redraw, which would get optimised to
  1546.  *    oblivion,
  1547.  *
  1548.  * trmsense(&y, &x)
  1549.  *    Returns the cursor position through its parameters
  1550.  *    after a possible manual change by the user.
  1551.  *
  1552.  * trmputdata(yfirst, ylast, indent, data)
  1553.  *     Fill lines {yfirst..ylast} with data, after skipping the initial
  1554.  *    'indent' positions. It is assumed that these positions do not contain
  1555.  *    anything dangerous (like standout cookies or null characters).
  1556.  *
  1557.  * trmscrollup(yfirst, ylast, by)
  1558.  *     Shift lines {yfirst..ylast} up by lines (down |by| if by < 0).
  1559.  *
  1560.  * trmsync(y, x)
  1561.  *     Call to output data to the terminal and set cursor position.
  1562.  *
  1563.  * trmbell()
  1564.  *    Send a (possibly visible) bell, immediately (flushing stdout).
  1565.  *
  1566.  * trmend()
  1567.  *     Obligatory termination call (resets tty modes etc.).
  1568.  *
  1569.  * You may call these as one or more cycles of:
  1570.  *     + trmstart
  1571.  *     +    zero or more times any of the other routines
  1572.  *     + trmend
  1573.  * Trmend may be called even in the middle of trmstart; this is necessary
  1574.  * to make it possible to write an interrupt handler that resets the tty
  1575.  * state before exiting the program.
  1576.  *
  1577.  * ADDITIONAL SPECIFICATIONS (ROUTINES FOR CHARACTER INPUT)
  1578.  *
  1579.  * trminput()
  1580.  *    Return the next input character (with its parity bit cleared
  1581.  *    if any).  This value is a nonnegative int.  Returns -1 if the
  1582.  *    input can't be read any more.
  1583.  *
  1584.  * trmavail()
  1585.  *    Return 1 if there is an input character immediately available,
  1586.  *    0 if not.  Return -1 if not implementable.
  1587.  *
  1588.  * trminterrupt()
  1589.  *    Return 1 if an interrupt has occurred since the last call to
  1590.  *    trminput or trmavail, 0 else.  [Currently not implemented.]
  1591.  *
  1592.  * trmsuspend()
  1593.  *    When called in the proper environment (4BSD with job control
  1594.  *    enabled), suspends the editor, temporarily popping back to
  1595.  *    the calling shell.  The caller should have called trmend()
  1596.  *    first, and must call trmstart again afterwards.
  1597.  *    BUG: there is a timing window where keyboard-generated
  1598.  *    signals (such as interrupt) can reach the program.
  1599.  */
  1600.